﻿unit TicTac;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, ScktComp, Buttons, ComCtrls;

type
  Tfttt = class(TForm)
    Panel11: TPanel;
    Panel12: TPanel;
    Panel13: TPanel;
    Panel21: TPanel;
    Panel22: TPanel;
    Panel23: TPanel;
    Panel31: TPanel;
    Panel32: TPanel;
    Panel33: TPanel;
    rdg_Spieler: TRadioGroup;
    Label1: TLabel;
    edt_Adresse: TEdit;
    edt_Port: TEdit;
    edt_nick: TEdit;
    GroupBox1: TGroupBox;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    btn_Verbinden: TButton;
    btn_Trennen: TButton;
    ClientSocket: TClientSocket;
    btn_Senden: TBitBtn;
    rtf_Chat: TRichEdit;
    rtf_log: TRichEdit;
    lb_Clients: TListBox;
    ListBox1: TListBox;
    edt_Nachricht: TEdit;
    Button4: TButton;
    Button6: TButton;
    Button7: TButton;
    Button8: TButton;
    Button9: TButton;
    GroupBox2: TGroupBox;
    fortschritt: TProgressBar;
    b_invite: TButton;
    b_edit: TButton;
    l_noconn: TLabel;
    b_purge1: TButton;
    b_purge2: TButton;
    procedure b_purge2Click(Sender: TObject);
    procedure b_purge1Click(Sender: TObject);
    procedure b_inviteClick(Sender: TObject);
    procedure b_editClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure PanelClick(Sender: TObject);
    procedure btn_VerbindenClickClick(Sender: TObject);
    procedure btn_TrennenClick(Sender: TObject);
    procedure btn_SendenClick(Sender: TObject);
    procedure ClientSocketConnect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
    procedure ClientSocketDisconnect(Sender: TObject;
      Socket: TCustomWinSocket);
    procedure Button4Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure Button9Click(Sender: TObject);
  private
    { Private-Deklarationen }
    procedure SetzeStatus;
    procedure Schicke(FixedMSG : string);
    procedure Logge(MSG : string);
    procedure ZeigeAn(MSG : string);
    procedure Delay(Milliseconds: Integer);
    procedure Lock;
    procedure Unlock;
    procedure PrepareGame;
    procedure CheckWinner;
    procedure ZeigeStand;
    procedure Protect;
    procedure Free;
    procedure AllesNeu(intensity: string);
	  procedure BesetztFrei;
  public
    { Public-Deklarationen }
  end;

var
  //TTT
  fttt: Tfttt;
  Feld: Array[1..3,1..3]of byte;
  Gew, counter, stand1, stand2, Werbinich, confirmed, connected: byte;
  //client
  myNick, enemyNick, Server: String;
  implementation

{$R *.dfm}

procedure Tfttt.Delay(Milliseconds: Integer);
var
  Tick: DWord;
  Event: THandle;
begin
  Event := CreateEvent(nil, False, False, nil);
  try
    Tick := GetTickCount + DWord(Milliseconds);
    while (Milliseconds > 0) and
          (MsgWaitForMultipleObjects(1, Event, False, Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do
    begin
      Application.ProcessMessages;
      if Application.Terminated then Exit;
      Milliseconds := Tick - GetTickcount;
    end;
  finally
    CloseHandle(Event);
  end;
end;

procedure Tfttt.BesetztFrei;
var
  newstat : string;
begin
  newstat := 'Status wird geändert zu: ';

  if Button9.Caption = 'Status: frei' then
  begin
    Schicke('b:b');
    Button9.Caption := 'Status: besetzt';
    newstat := newstat + 'besetzt';
    Schicke('a:' + myNick + ' hat gerade keine Zeit für ein Spiel.');
  end
  else
  begin
    Button9.Caption := 'Status: frei';
    newstat := newstat + 'frei';
    Schicke('b:f');
    Schicke('a:' + myNick + ' hat nun wieder Zeit für ein Spiel.');
  end;
  Logge(newstat+'.');
end;

procedure Tfttt.Protect;
var i,j:byte;
begin
for i:=1 to 3 do
  begin
    for j:=1 to 3 do
      begin
        TPanel(FindComponent('Panel'+IntToStr(i)+IntToStr(j))).Enabled := false;
      end;
  end;
end;

procedure Tfttt.Free;
var i,j:byte;
begin
for i:=1 to 3 do
  begin
    for j:=1 to 3 do
      begin
        TPanel(FindComponent('Panel'+IntToStr(i)+IntToStr(j))).Enabled := true;
      end;
  end;
end;

//Status setzen
procedure Tfttt.SetzeStatus;
begin
  btn_Verbinden.Enabled:= not ClientSocket.Active;
  btn_Trennen.Enabled  := ClientSocket.Active;
  btn_Senden.Enabled   := ClientSocket.Active;
end;

procedure Tfttt.Logge(MSG : string);
begin
  rtf_log.Lines.Add(MSG);
  rtf_log.Perform(WM_VSCROLL,SB_BOTTOM,0);
end;

procedure Tfttt.ZeigeAn(MSG : string);
begin
  rtf_Chat.Lines.Add(MSG);
  rtf_Chat.Perform(WM_VSCROLL,SB_BOTTOM,0);
end;

procedure Tfttt.Schicke(FixedMSG : string);
begin
  Sleep(450);
  ClientSocket.Socket.SendText(FixedMSG);
  Logge('Sende zum Server: ' + FixedMSG);
end;

procedure Tfttt.Lock;
begin
lb_Clients.Enabled := false;
ListBox1.Enabled := false;
Button8.Enabled := false;
Button9.Enabled := false;
Button4.Enabled := false;
Button6.Enabled := false;
b_invite.Enabled := false;
b_purge1.Enabled := false;
b_purge2.Enabled := false;
rtf_log.Lines.Add('verriegle...');
end;

procedure Tfttt.Unlock;
begin
lb_Clients.Enabled := true;
ListBox1.Enabled := true;
Button8.Enabled := true;
Button9.Enabled := true;
Button4.Enabled := true;
Button6.Enabled := true;
b_invite.Enabled := true;
b_purge1.Enabled := true;
b_purge2.Enabled := true;
rtf_log.Lines.Add('räume auf...');
end;

procedure Tfttt.PrepareGame;
var i,j:byte;     //zum starten
begin
rtf_log.Lines.Add('initialisiere Spiel...');
  counter := 0;
  connected := 1;
  BesetztFrei;
  for i:=1 to 3 do
    begin
      for j:=1 to 3 do
        begin
          TPanel(FindComponent('Panel'+IntToStr(i)+IntToStr(j))).Enabled := true;
          Feld[i,j] := 0;
        end;
    end;
end;



//onConnect
procedure Tfttt.ClientSocketConnect(Sender: TObject;
  Socket: TCustomWinSocket);

begin
  fortschritt.Position := 70;
  Server:= Socket.RemoteHost + ' ' + Socket.RemoteAddress;
  fortschritt.Position := 80;
  Logge('Verbunden mit Server ' + Server);

  myNick := edt_Nick.Text;

  Schicke('c:' + edt_Nick.Text);
  Schicke('l:');
  Schicke('o:');
  fortschritt.Position := 90;

  Schicke('a:' + myNick + ' hat Zeit für ein Spiel.');

  SetzeStatus;
  fortschritt.Position := 0;
  btn_Verbinden.Visible := false;
  btn_Trennen.Visible := true;
  rtf_Chat.Visible := true;
  rtf_log.Visible := true;
  edt_Nachricht.Visible := true;
  lb_Clients.Visible := true;
  ListBox1.Visible := true;
  Button7.Visible := true;
  Button8.Visible := true;
  btn_Senden.Visible := true;
  b_invite.Visible := true;
  b_purge1.Visible := true;
  b_purge2.Visible := true;
  l_noconn.Visible := false;
end;

//Nachricht lesen und Auswerten
procedure Tfttt.ClientSocketRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
Trenner, i, j : byte;
Art, Nachricht, Wer, Inhalt, Hilfe, msg, Ausgabe : string;
Laenge, ppos, UserEingabe : integer;
  Spielstein: string;

begin
   Nachricht := Socket.ReceiveText;
   Logge('Erhalten:' + Nachricht);

   Art := Nachricht[1];
   Inhalt := Copy(Nachricht,3,length(Nachricht)-2);

   If (Art = 'a') or (Art = 'p') then
   begin
      Trenner := Pos(':',Inhalt);

      Wer    := Copy(Inhalt,1,Trenner-1);
      Inhalt := Copy(Inhalt,Trenner+1,length(Inhalt)-Trenner);
   end;

   If Art = 's' then
   begin
      Inhalt := Copy(Inhalt,Pos('s:', Inhalt) + 1, Length(Inhalt) - Pos('s:', Inhalt) + 1);
      rtf_Chat.SelAttributes.Color := clGreen;
      Ausgabe := 'Vom Server: ' + Inhalt;
   end;

   If Art = 'c' then
   begin
      Inhalt := Copy(Inhalt,Pos('c:', Inhalt) + 1, Length(Inhalt) - Pos('c:', Inhalt) + 1);
      rtf_Chat.SelAttributes.Color := clGreen;
      Ausgabe := 'Message of the day: ' + Inhalt;
   end;

   If Art = 'a' then
   begin
      //Laenge := (length(Inhalt)) DIV 2;
      //ppos := 0;
      //msg := '';
         //for ppos := 1 to Laenge do
         //begin
         //msg := msg + Inhalt[ppos];
         //end;
      rtf_Chat.SelAttributes.Color := clBlue;
      Ausgabe := 'an alle: ' + Wer + ': ' + Inhalt; //statt inhalt[] geht bei doppelübertragung msg
   end;

   If Art = 'p' then
   begin
      if Copy(Inhalt, 1, 2) = 'i:' then
        begin
          if Copy(Inhalt, 1, 7) = 'i:ttt:?' then //nehme ich an?
            begin
              rtf_Chat.SelAttributes.Color := clBlue;
              Ausgabe := 'Spielanfrage von ' + Wer + ' erhalten.';
              UserEingabe := MessageDlg('Hallo ' + myNick + ', möchtest du mit mir TicTacToe spielen?' + #10#13 + Wer, mtConfirmation, [mbYes, mbNo], 0);
              if UserEingabe = mrYes {mrNo} then
                begin
                  Lock;                //both should have x as symbol
                  Schicke('p:' + Wer + ':i:ttt:ja');
                  Logge('Bitte warte 1 sec...');
                  Delay(1000);
                  enemyNick := Wer;
                  If confirmed = 1 then
                  begin
                    PrepareGame;
                    Werbinich := 1; //Spieler 1
                    ShowMessage('Danke für deine Spielteilnahme! Du bist Spieler 1 und beginnst.');
                  end
                  else
                  begin
                    ShowMessage('Der Mitspieler ist nicht erreichbar.');
                    Unlock;
                  end;
                end
                else
                begin
                  Schicke('p:' + Wer + ':i:ttt:nein');
                  Unlock;
              end;
            end;

          if Copy(Inhalt, 1, 8) = 'i:ttt:ja' then
            begin  //hat der Eingeladene angenommen?
              Werbinich := 2; //Spieler 2
              enemyNick := Wer;
              Schicke('p:' + Wer + ':i:ttt:confirmed');
              Logge(enemyNick + ' hat die Anfrage angenommen.');
              ShowMessage(enemyNick + ' hat die Anfrage angenommen. Du bist Spieler 2 und wartest nun auf deinen Zug.');
              PrepareGame;
              Lock;
              Protect;
            end;

            if Copy(Inhalt, 1, 12) = 'i:ttt:stein:' then
            begin
              i := StrToInt(Copy(Inhalt, 13, 1));
              j := StrToInt(Copy(Inhalt, 14, 1));
              if Feld[i,j] = 0 then
                begin
                  //im Array Feld syncronisieren
                  Feld[i,j] := 10;

                  //Spieler ermittelt
                  Spielstein := 'O';

                  //Stein setzen
                  TPanel(FindComponent('Panel'+IntToStr(i)+IntToStr(j))).Caption := Spielstein;
                  //feld blockieren
                  TPanel(FindComponent('Panel'+IntToStr(i)+IntToStr(j))).Enabled := false;

                  CheckWinner;


                  if (Gew = 0) and (counter >= 9) then
                    begin
                      ShowMessage('Du bist jetzt am Zug.');
                      rdg_Spieler.ItemIndex := 0;
                      Free;
                    end;
                  end
                  else
                  begin
                    Schicke('p:' + Wer + 'Cheaten ist verboten!!!');
                  end;
            end;

          if Copy(Inhalt, 1, 9) = 'i:ttt:nein' then
            begin  //hat der Eingeladene abgelehnt?
              AllesNeu('komplett');
              Logge(Wer + ' hat die Anfrage abgelehnt.');
              Unlock;
            end;

          if Copy(Inhalt, 1, 15) = 'i:ttt:confirmed' then
            begin
            Logge('Confirmation recieved.');
            confirmed := 1;
            end;

        end
        else
        begin
          rtf_Chat.SelAttributes.Color := clRed;
          Ausgabe := 'An mich von ' + Wer + ': ' + Inhalt;
        end;
   end;

   if Art = 'l' then
   begin
      lb_Clients.Clear;
      lb_Clients.Items.Add('An alle');
      for i := 1 to length(Inhalt) do
      begin
         if Inhalt[i] <> '~' then
            Hilfe := Hilfe + Inhalt[i]
         else
         begin
            if Hilfe <> edt_nick.Text then
               lb_Clients.Items.Add(Hilfe);
            Hilfe := '';
         end;
      end;
   end;
   if Art = 't' then
   begin
      Ausgabe := Inhalt;
   end;
   // Liste freier Spieler
   if Art = 'o' then
   begin
      ListBox1.Clear;
      for i := 1 to length(Inhalt) do
      begin
         if Inhalt[i] <> '~' then
            Hilfe := Hilfe + Inhalt[i]
         else
         begin
            if Hilfe <> edt_nick.Text then
               ListBox1.Items.Add(Hilfe);
            Hilfe := '';
         end;
      end;
   end;

   if length(Ausgabe) >= 2 then ZeigeAn(Ausgabe);
   rtf_Chat.SelAttributes.Color := clBlack;
end;

procedure Tfttt.ZeigeStand();
begin
  Label1.Caption := IntToStr(stand1) + ' : ' + IntToStr(stand2);
end;

procedure Tfttt.CheckWinner();
var
  i,j:byte;
  sm:byte;
begin
  sm := 0;
  Gew := 0;

  for j := 1 to 3 do
  begin
    sm := sm + Feld[1,j];
  end;

    if sm = 3         //einmal eingerückt und auf mehrere zeilen verteilt
      then            //damit man die struktur versteht
        Gew := 1
      else
        if sm = 30
        then
          Gew := 2
        else
          sm := 0;

  for j := 1 to 3 do
  begin
    sm := sm + Feld[2,j];
  end;
    if sm = 3 then Gew := 1 else if sm = 30 then Gew := 2 else sm := 0;

  for j := 1 to 3 do
  begin
    sm := sm + Feld[3,j];
  end;
    if sm = 3 then Gew := 1 else if sm = 30 then Gew := 2 else sm := 0;

  for j := 1 to 3 do
  begin
    sm := sm+Feld[j,1];
  end;
    if sm = 3 then Gew := 1 else if sm = 30 then Gew := 2 else sm := 0;

  for j := 1 to 3 do
  begin
    sm := sm + Feld[j,2];
  end;
    if sm = 3 then Gew := 1 else if sm = 30 then Gew := 2 else sm := 0;

  for j := 1 to 3 do
  begin
    sm := sm + Feld[j,3];
  end;
    if sm = 3 then Gew := 1 else if sm = 30 then Gew := 2 else sm := 0;

  for j := 1 to 3 do
  begin
    sm := sm + Feld[j,j];
  end;
    if sm = 3 then Gew := 1 else if sm = 30 then Gew := 2 else sm := 0;

  for j := 1 to 3 do
  begin
    sm := sm + Feld[4-j,j];
  end;
    if sm = 3 then Gew := 1 else if sm = 30 then Gew := 2;

    counter := counter + 1;

    if (Gew = 1) or (Gew = 2) then
      begin
        if rdg_Spieler.ItemIndex = 0 then begin
          ShowMessage('Du gewinnst!');
          stand1 := stand1 + 1;
        end
        else
        begin
          ShowMessage('Dein Gegner gewinnt.');
          stand2 := stand2 + 1;
        end;
        Protect;
        ZeigeStand;
        Unlock;
        AllesNeu('runde');
      end
      else
      begin
        if (Gew = 0) and (counter = 9) then
          begin
            ShowMessage('Keiner gewinnt.');
            Protect;
            Unlock;
            AllesNeu('runde');
          end;
      end;
end;

procedure Tfttt.PanelClick(Sender: TObject);
var
  Spielstein: string;
  i,j:byte;

begin
  i := StrToInt(copy(TPanel(Sender).Name,6,1));
  j := StrToInt(copy(TPanel(Sender).Name,7,1));

  if (rdg_Spieler.ItemIndex = 0) and (Feld[i,j] = 0) then
  begin

    Protect;

    //im Array Feld syncronisieren
    //override: Feld[i,j] := 10 * (rdg_Spieler.ItemIndex + 1);
    Feld[i,j] := 1;

    //Spieler ermittelt
    Spielstein := 'X';

    //position senden
    Schicke(
      'p:'
      + enemyNick
      + ':i:ttt:stein:'
      + IntToStr(i)
      + IntToStr(j)
    );

    //Stein setzen
    TPanel(Sender).Caption := Spielstein;

    //feld blockieren
    TPanel(Sender).Enabled := false;

    CheckWinner;

	  //Zeichen geändert
	  rdg_Spieler.ItemIndex := 1;
  end;
end;


procedure Tfttt.AllesNeu(intensity: string);
var i,j:byte;
begin
  Werbinich := 0;
  confirmed := 0;

  if intensity = 'komplett' then
  begin
    stand1 := 0;
    stand2 := 0;
    ZeigeStand();
  end;

  //um exploiting auszuschließen
  rdg_Spieler.ItemIndex := 0;

  for i:=1 to 3 do
    begin
      for j:=1 to 3 do
        begin
          Feld[i,j] := 0;
          TPanel(FindComponent('Panel'+IntToStr(i)+IntToStr(j))).Enabled := false;
          TPanel(FindComponent('Panel'+IntToStr(i)+IntToStr(j))).Caption := '-';
        end;
    end;

end;

procedure Tfttt.FormCreate(Sender: TObject);
begin
  AllesNeu('komplett');
  Randomize;
  edt_Nick.Text := edt_Nick.Text + IntToStr(Random(9));
end;

//Verbinden
procedure Tfttt.btn_VerbindenClickClick(Sender: TObject);
begin
  With ClientSocket do begin
    Address:= edt_Adresse.Text;
    fortschritt.Position := 7;
    Port:= StrToInt(edt_Port.Text);
    edt_Adresse.Enabled := false;
    edt_Port.Enabled := false;
    fortschritt.Position := 20;
    Logge('Stelle Verbindung zum Server ' + Address + ' her...');
    fortschritt.Position := 35;
    Open;
    fortschritt.Position := 50;
  end;
end;

procedure Tfttt.btn_SendenClick(Sender: TObject);
var
   AnWen, Anzeige, AnzeigeZwei, Nachricht: string;
   Laenge, pos : integer;

begin
   pos := 0;
   Anzeige := 'Von mir an ';
   Laenge := Length(edt_Nachricht.Text);
   Nachricht := edt_Nachricht.Text;

 if Laenge = 0 then
   begin
     ShowMessage('Bitte nicht spammen!');
   end
   else
	 begin
	 if lb_Clients.ItemIndex < 1 then
     begin
       AnWen := 'a:';
       Anzeige := Anzeige + 'alle:';
     end

     else
     begin
       Anzeige := Anzeige + lb_Clients.Items[lb_Clients.ItemIndex] + ':';
       AnWen := 'p:' + lb_Clients.Items[lb_Clients.ItemIndex] + ':';
     end;



     //AnzeigeZwei := NachrichtZwei;  //log-Ausgabe
     Anzeige := Anzeige + edt_Nachricht.Text; //unbearbeitet

     ZeigeAn(Anzeige);
     Logge('sende Text:' + Anzeige); // + AnzeigeZwei

     Schicke('l:');
     Schicke('o:');
     Schicke(AnWen + Nachricht);
  //      end;


     edt_Nachricht.Clear;
     edt_Nachricht.SetFocus;
   end;
end;

//Trennen
procedure Tfttt.btn_TrennenClick(Sender: TObject);
begin
  Logge('Trenne Verbindung zum Server ' + Server + '...');
  ClientSocket.Close;
  SetzeStatus;
  btn_Verbinden.Visible := true;
  btn_Trennen.Visible := false;
  rtf_Chat.Visible := false;
  rtf_log.Visible := false;
  edt_Nachricht.Visible := false;
  lb_Clients.Visible := false;
  ListBox1.Visible := false;
  Button7.Visible := false;
  Button8.Visible := false;
  btn_Senden.Visible := false;
  b_invite.Visible := false;
  b_purge1.Visible := false;
  b_purge2.Visible := false;
  l_noconn.Visible := true;
end;

//onDisconnect
procedure Tfttt.ClientSocketDisconnect(Sender: TObject;
  Socket: TCustomWinSocket);
begin
  Logge('Der Server ' + Server + ' hat die Verbindung unterbrochen.');
  btn_Trennen.Enabled := true;
  SetzeStatus;
end;

procedure Tfttt.Button4Click(Sender: TObject);
begin
  edt_adresse.Text := '62.108.37.193';
end;

procedure Tfttt.Button6Click(Sender: TObject);
begin
  edt_adresse.Text := '127.0.0.1';
end;

procedure Tfttt.Button7Click(Sender: TObject);
begin
   Schicke('l:');
   Logge('Gesamtliste wird abgerufen...');
end;

procedure Tfttt.Button8Click(Sender: TObject);
begin
   Schicke('o:');
   Logge('Freie Spieler werden abgerufen...');
end;

procedure Tfttt.Button9Click(Sender: TObject);
begin
  BesetztFrei;
end;

procedure Tfttt.b_editClick(Sender: TObject);
begin
  edt_Adresse.Enabled := not edt_Adresse.Enabled;
  edt_Port.Enabled := not edt_Port.Enabled;
  //edt_nick.Enabled := true;
end;

procedure Tfttt.b_inviteClick(Sender: TObject);
var Anzeige, AnWen : string;
begin
  Anzeige := 'Anfrage an ';
  Anzeige := Anzeige + ListBox1.Items[ListBox1.ItemIndex];
  AnWen := 'p:' + ListBox1.Items[ListBox1.ItemIndex] + ':';

  ZeigeAn(Anzeige);
  Logge('sende Anfrage: ' + Anzeige);

  Schicke('l:');
  Schicke('o:');
  Schicke(AnWen + 'i:ttt:?'); //i: für anfrage

  //remember the sent request
  Lock;
end;

procedure Tfttt.b_purge1Click(Sender: TObject);
begin
  rtf_Chat.Lines.Clear;
end;

procedure Tfttt.b_purge2Click(Sender: TObject);
begin
  rtf_log.Lines.Clear;
end;

end.
